﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.IO.Ports;

using AForge.Video;
using AForge.Video.DirectShow;

namespace GUI
{
    public partial class Form1 : Form
    {
        private bool DeviceExist;
        private FilterInfoCollection videoDevices;
        private VideoCaptureDevice videoSource;
        private SimpleJoystick JoyStick;

        private SerialPort Robot;

        private delegate void XYChangeHandler();
        private event XYChangeHandler XYChangeEvent;

        private int JoystickX;
        private int JoystickY;

        private int JoystickXOld;
        private int JoystickYOld;

        private string JoystickDirX;
        private string JoystickDirY;

        private byte[] rxData;
        private byte[] txData;

        private string dataRecieved;
        private string[] dataLines;

        delegate void SetTextCallback(string text);

        public Form1()
        {
            InitializeComponent();
            Main();
        }

        private void Main()
        {
            getComPortList();
            getCamList();
            XYChangeEvent += JoystickXYMove;
        }
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            CloseVideoSource();
        }

        #region video
        private void getCamList()
        {
            try
            {
                videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
                comboBoxCaptureDevices.Items.Clear();
                if (videoDevices.Count == 0)
                    throw new ApplicationException();

                DeviceExist = true;
                foreach (FilterInfo device in videoDevices)
                {
                    comboBoxCaptureDevices.Items.Add(device.Name);
                }
                comboBoxCaptureDevices.SelectedIndex = 0;
            }
            catch (ApplicationException)
            {
                DeviceExist = false;
                comboBoxCaptureDevices.Items.Add("No capture device on your system");
            }
        }
        private void buttonStartStopVideo_Click(object sender, EventArgs e)
        {
            if (buttonStartStopVideo.Text == "&Start")
            {
                if (DeviceExist)
                {
                    videoSource = new VideoCaptureDevice(videoDevices[comboBoxCaptureDevices.SelectedIndex].MonikerString);
                    videoSource.NewFrame += new NewFrameEventHandler(video_NewFrame);
                    CloseVideoSource();
                    videoSource.DesiredFrameSize = new Size(640, 480);
                    videoSource.Start();
                    toolStripStatusLabelStatus.ForeColor = Color.Green;
                    toolStripStatusLabelStatus.Text = "Device running...";
                    buttonStartStopVideo.Text = "&Stop";
                    timerFPS.Enabled = true;
                }
                else
                {
                    toolStripStatusLabelStatus.ForeColor = Color.Red;
                    toolStripStatusLabelStatus.Text = "Error: No Device selected.";
                }
            }
            else
            {
                if (videoSource.IsRunning)
                {
                    timerFPS.Enabled = false;
                    CloseVideoSource();
                    pictureBoxVideo.Image = null;
                    pictureBoxVideo.Invalidate();
                    toolStripStatusLabelStatus.ForeColor = Color.Red;
                    toolStripStatusLabelStatus.Text = "Device idle...";
                    buttonStartStopVideo.Text = "&Start";
                }
            }
        }
        private void buttonRefreshVideo_Click(object sender, EventArgs e)
        {
            getCamList();
        }
        private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
        {
            Bitmap img = (Bitmap)eventArgs.Frame.Clone();
            img.RotateFlip(RotateFlipType.RotateNoneFlipX);
            pictureBoxVideo.Image = img;
        }
        private void CloseVideoSource()
        {
            if (!(videoSource == null))
                if (videoSource.IsRunning)
                {
                    videoSource.SignalToStop();
                    videoSource = null;
                }
        }
        private void pictureBoxVideo_Paint(object sender, PaintEventArgs e)
        {
            foreach (Control C in groupBoxVideo.Controls)
            {
                if (C is Label)
                {
                    Label L = (Label)C;
                    L.Visible = false;
                    e.Graphics.DrawString(L.Text, L.Font, new SolidBrush(L.ForeColor), L.Left - pictureBoxVideo.Left, L.Top - pictureBoxVideo.Top);
                }
            }
        }
        private void buttonCaptureImage_Click(object sender, EventArgs e)
        {
            try
            {
                Image image = pictureBoxVideo.Image;
                image.Save(@"c:\" + DateTime.Now.ToString("yyyy_MM_dd.HH_mm_ss") + ".png", ImageFormat.Png);
            }

            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }
        #endregion

        #region timers
        private void timerFPS_Tick(object sender, EventArgs e)
        {
            labelCameraFPS.Text = videoSource.FramesReceived + " FPS";
        }
        private void timerJoystick_Tick(object sender, EventArgs e)
        {
            JoystickY = JoyStick.State.RotationZ;
            JoystickX = JoyStick.State.Y;

            if ((JoystickX != JoystickXOld) || (JoystickY != JoystickYOld))
            {
                XYChangeEvent();
            }

            JoystickXOld = JoystickX;
            JoystickYOld = JoystickY;
        }
        #endregion

        #region joystick
        private void buttonConnectJoystick_Click(object sender, EventArgs e)
        {
            JoyStick = null;
            JoyStick = new SimpleJoystick();
            timerJoystick.Enabled = true;
        }
        private void buttonDisconnectJoystick_Click(object sender, EventArgs e)
        {
            JoyStick.Release();
            timerJoystick.Enabled = false;
        }
        private void JoystickXYMove()
        {
            if (!JoyStick.State.IsPressed(0))
            {
                if (JoystickX > 0)
                {
                    JoystickX = 100 - JoystickX;
                    JoystickDirX = "R";
                    JoystickDirY = "L";
                }

                else if (JoystickX < 0)
                {
                    JoystickX = 100 + JoystickX;
                    JoystickDirX = "L";
                    JoystickDirY = "R";
                }

                else if (JoystickX > -90 || JoystickX > 90)
                {
                    JoystickX = 99;
                }
            }

            if (JoyStick.State.IsPressed(0))
            {
                if (JoystickY > 0)
                {
                    JoystickX = 100 - JoystickY;
                    Console.WriteLine("Y " + JoystickY);
                    Console.WriteLine("X " + JoystickX);
                    JoystickDirX = "R";
                    JoystickDirY = "R";
                }

                if (JoystickY < 0)
                {
                    JoystickX = 100 + JoystickY;
                    JoystickDirX = "L";
                    JoystickDirY = "L";
                }

                else if (JoystickY < -10 || JoystickY < 10)
                {
                    JoystickX = 99;
                }
            }

            label6.Text = JoystickX.ToString();
            label7.Text = JoystickY.ToString();

            txData = Encoding.UTF8.GetBytes(JoystickDirX + ";" + JoystickX + ";" + JoystickDirY + ";" + JoystickX);
            Robot.Write(txData, 0, txData.Length);

            Thread.Sleep(100);
        }
        #endregion

        #region serial port
        private void getComPortList()
        {
            foreach (string str in SerialPort.GetPortNames())
            {
                comboBoxComPorts.Items.Add(str);
            }

            comboBoxComPorts.SelectedIndex = 1;
        }
        private void buttonOpenComPort_Click(object sender, EventArgs e)
        {
            if (buttonOpenComPort.Text == "Open")
            {
                Robot = new SerialPort(comboBoxComPorts.SelectedItem.ToString(), 115200);
                Robot.Open();
                Robot.DataReceived += Robot_DataReceived;

                buttonOpenComPort.Text = "Close";
            }

            else
            {
                timerJoystick.Enabled = false;
                Robot.Close();
                buttonOpenComPort.Text = "Open";
            }
        }
        private void buttonSendData_Click(object sender, EventArgs e)
        {
            txData = Encoding.UTF8.GetBytes(textBox1.Text);
            Robot.Write(txData, 0, txData.Length);
        }
        private void Robot_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            rxData = new byte[Robot.BytesToRead];
            Robot.Read(rxData, 0, rxData.Length);

            dataRecieved = new String(Encoding.UTF8.GetChars(rxData));

            if (dataRecieved.IndexOf('\n') > 0)
            {
                dataLines = dataRecieved.Split(';');
                if (dataLines.Length == 4)
                {
                    SetLabelCameraVText(dataLines[0]);
                    SetLabelCameraA1Text(dataLines[1]);
                    SetLabelCameraA2Text(dataLines[2]);
                    SetLabelCameraTMPText(dataLines[3]);
                }
            }
            rxData = null;
            dataLines = null;
            dataRecieved = null;
        }
        #endregion

        #region Threadsafe label.text methods
        private void SetLabelCameraVText(string text)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.textBox1.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetLabelCameraVText);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.labelCameraV.Text = text;
            }
        }
        private void SetLabelCameraA1Text(string text)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.textBox1.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetLabelCameraA1Text);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.labelCameraA1.Text = text;
            }
        }
        private void SetLabelCameraA2Text(string text)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.textBox1.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetLabelCameraA2Text);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.labelCameraA2.Text = text;
            }
        }
        private void SetLabelCameraTMPText(string text)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.textBox1.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetLabelCameraTMPText);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.labelCameraTMP.Text = text;
            }
        }
        #endregion
    }
}
